Skip to content

feat: per-user LLM provider configuration with toggle-based default selection and pre-flight validation - #16

Draft
ZhenhangTung with Copilot wants to merge 10 commits into
mainfrom
copilot/add-model-configuration-page
Draft

feat: per-user LLM provider configuration with toggle-based default selection and pre-flight validation#16
ZhenhangTung with Copilot wants to merge 10 commits into
mainfrom
copilot/add-model-configuration-page

Conversation

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor

Per-user LLM model provider configuration with encrypted API keys

Completed

  • Backend: Encryption utility, UserLLMConfig model, serializers, views, URLs
  • Backend: Per-user config priority in get_provider_config() / get_default_provider()
  • Backend: is_default field with single-default enforcement per user
  • Backend: /api/accounts/llm-configs/readiness/ endpoint
  • Backend: 75 tests pass
  • Frontend: Models tab in Settings with provider cards
  • Frontend: Pre-flight validation before workspace creation
  • Frontend: Warning banner + auto-open Settings → Models on failure
  • Frontend: Toggle switch for default provider selection (mutually exclusive)
  • Frontend: Subtle "Remove" link for API key deletion
  • Frontend: Remove "(fixed)" suffix from model name display
  • Frontend: Update model name to Gemini 3 Pro
  • Frontend: i18n translations (EN + zh-CN)

Screenshots

Toggle switches (unconfigured) Model name without "(fixed)" suffix
unconfigured no-fixed
Gemini toggled ON OpenRouter toggled ON (Gemini auto OFF)
gemini-default openrouter-default
Pre-flight warning + auto-open Settings
preflight
Original prompt

在用户的设置页面创建模型的配置界面。
当前模型供应商和模型是环境变量配置,这代表全局都是使用同一个 API Key。
我希望是:

  1. 每个用户都可以自己选择和配置属于模型供应商和模型
  2. 首期模型供应商提供的是配置里定义好的 Gemini & OpenRouter,模型固定为 Gemini 3 Pro(这些都是已有的内容)
  3. 模型的密钥需要在数据库加密保存,以防数据库攻击,但读取请求大模型 api 的时候又需要能够获取到解密后的值。

💬 We'd love your input! Share your thoughts on Copilot coding agent in our 2 minute survey.

Copilot AI and others added 3 commits February 26, 2026 05:37
…d API keys

- Add cryptography dependency for Fernet encryption
- Create encryption utility (encrypt_value/decrypt_value)
- Add UserLLMConfig model with encrypted API key storage
- Add serializers, views, and URL routes for CRUD operations
- Update LLM config to support per-user provider configuration
- Add comprehensive tests for encryption, model, and API endpoints

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>
…configuration

- Create llmConfig.js service for LLM config CRUD operations
- Add Models tab with CpuChipIcon to settings dialog in ConsoleLayout
- Add provider cards for Gemini and OpenRouter with status badges
- Add masked API key display for configured providers
- Add inline API key update form and delete confirmation dialog
- Add i18n translations (EN + zh-CN) for model configuration UI

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>
Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>
Copilot AI changed the title [WIP] Add model configuration interface for user settings feat: per-user LLM provider configuration with encrypted API key storage Feb 26, 2026
@ZhenhangTung

Copy link
Copy Markdown
Collaborator

@copilot

Requirement 1: Default Model Provider Selection
Given that the interface currently supports 2 model providers, we need to implement a default provider selection mechanism. Without a designated default, the system cannot determine which provider to invoke during model calls.

Requirement 2: Pre-flight Configuration Validation
Prior to creating a workspace, the system must verify that the user has:

  • Configured a default model provider
  • Set up valid API credentials

If validation fails, the UI should:

  1. Display a warning notification
  2. Automatically open the provider configuration modal
  3. Guide the user through setup to prevent runtime errors during image analysis

Copilot AI and others added 3 commits February 26, 2026 06:22
- Add is_default field to UserLLMConfig model with single-default enforcement
- Add /api/accounts/llm-configs/readiness/ endpoint for pre-flight checks
- Update get_default_provider() to prioritize user's explicit default
- Add migration for is_default field
- Add 6 new tests (default provider, readiness endpoint)

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>
- Add "Set as default" / "Default" badge to provider cards in Models tab
- Add pre-flight LLM readiness check before workspace creation
- Show warning banner and auto-open Settings → Models when validation fails
- Add checkLLMReadiness() service function
- Add i18n translations (EN + zh-CN) for default/readiness UI strings
- ConsoleLayout accepts openSettingsTab/onSettingsClosed props

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>
- Move readiness URL before uuid pattern to avoid routing conflict
- Extract inline onClose to handleCloseSettings function
- Add logger.warning to exception catches in LLM config module

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot

Requirement 1: Default Model Provider Selection...

Implemented both requirements in commits 50f28dc through c21fec3:

Requirement 1 — Default Provider Selection:

  • Added is_default field to UserLLMConfig with single-default enforcement per user
  • Each provider card now shows a "Set as default" button and a "Default" badge
  • get_default_provider() prioritizes the user's explicitly marked default

Requirement 2 — Pre-flight Configuration Validation:

  • Added /api/accounts/llm-configs/readiness/ endpoint
  • DesignWorkshop.handleCreateNew() calls readiness check before opening the create modal
  • On failure: warning banner is displayed and Settings → Models tab auto-opens
Pre-flight warning + auto-open Settings Default badge after configuration
warning default

Copilot AI changed the title feat: per-user LLM provider configuration with encrypted API key storage feat: per-user LLM provider configuration with default selection and pre-flight validation Feb 26, 2026
@ZhenhangTung

Copy link
Copy Markdown
Collaborator

@copilot

UX Logic Question: Why is there a "Delete" button for the default provider? Does it mean "Remove default status" or "Delete the provider entirely"? This is ambiguous.

Proposed Solution: Replace the delete button with a toggle switch component for default provider selection:

  • Only one provider can be active/default at a time (mutually exclusive)
  • Initial state: Gemini toggle is ON (default), OpenRouter toggle is OFF
  • When user turns ON OpenRouter, automatically turn OFF Gemini (and vice versa)
  • This eliminates the confusion of "deleting" vs "deselecting"

…itch for default provider

- Replace "Set as default" button and "Delete" button with Headless UI Switch toggle
- Toggle is disabled when provider has no API key configured
- Only one provider can be ON at a time (mutually exclusive via backend enforcement)
- Prevent toggling OFF the current default (must always have one)
- Move "Remove" (delete API key) to subtle text link next to masked key
- Add aria-label to Remove button for accessibility
- Update i18n translations

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot

UX Logic Question: Why is there a "Delete" button for the default provider? Does it mean "Remove default status" or "Delete the provider entirely"? This is ambiguous....

Replaced the "Delete"/"Set as default" buttons with a Headless UI Switch toggle in fa7f34b:

  • Only one provider toggle can be ON at a time (mutually exclusive — backend clears the other on save)
  • Toggle is disabled/greyed out for unconfigured providers (no API key)
  • Cannot toggle OFF the current default (always one active)
  • "Remove" (delete API key) moved to a subtle text link next to the masked key
Unconfigured (toggles disabled) Gemini ON, OpenRouter OFF OpenRouter ON (Gemini auto OFF)
unconfigured gemini openrouter

Copilot AI changed the title feat: per-user LLM provider configuration with default selection and pre-flight validation feat: per-user LLM provider configuration with toggle-based default selection and pre-flight validation Feb 26, 2026
@ZhenhangTung

Copy link
Copy Markdown
Collaborator

@copilot no need to add a (fixed) string suffix after the model name

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot no need to add a (fixed) string suffix after the model name

Removed the "(fixed)" suffix in 64d7ec4. Model name now displays as "Gemini 2.5 Pro" in both EN and zh-CN.

no-fixed-suffix

@ZhenhangTung

Copy link
Copy Markdown
Collaborator

@copilot pls use Gemini 3 Pro instead of 2.5 Pro

Co-authored-by: ZhenhangTung <8170159+ZhenhangTung@users.noreply.github.com>

Copilot AI commented Feb 26, 2026

Copy link
Copy Markdown
Contributor Author

@copilot pls use Gemini 3 Pro instead of 2.5 Pro

Updated to "Gemini 3 Pro" in both EN and zh-CN translations in e3b0d53.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants